package com.nine.news.modular.user.service.impl;

import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.nine.news.constants.RedisKey;
import com.nine.news.entity.ServerLog;
import com.nine.news.entity.User;
import com.nine.news.entity.UserInfo;
import com.nine.news.entity.UserMsg;
import com.nine.news.enums.ConfigEnum;
import com.nine.news.enums.ServerType;
import com.nine.news.enums.UserMsgType;
import com.nine.news.mapper.*;
import com.nine.news.modular.user.param.UserFindPasswdParam;
import com.nine.news.modular.user.param.UserLoginCodeParam;
import com.nine.news.modular.user.param.UserLoginNameParam;
import com.nine.news.modular.user.param.UserLogonParam;
import com.nine.news.modular.user.service.UserService;
import com.nine.news.response.R;
import com.nine.news.util.JwtUtil;
import com.nine.news.util.PhoneUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * @Author: szhan
 * @Date: 2023/03/18/2:20 PM
 * @Description:
 */
@Service
public class UserServiceImpl implements UserService {
    @Resource
    private UserMapper mapper;
    @Resource
    private ConfigMapper configMapper;
    @Resource
    private StringRedisTemplate template;
    @Resource
    private UserMsgMapper msgMapper;
    @Resource
    private ServerLogMapper logMapper;
    @Resource
    private UserInfoMapper infoMapper;

    @Override
    public R register(UserLogonParam param) {
        if (param != null) {
            if (template.opsForSet().isMember(RedisKey.AUTH_ADD_PHONE, param.getPhone())) {
                User user = mapper.selectOne(new QueryWrapper<User>().eq("phone", param.getPhone()).or().eq("name", param.getName()));
                if (user == null) {
                    user = new User();
                    BeanUtils.copyProperties(param, user);
                    user.init();
                    user.setPassword(SecureUtil.rsa(configMapper.search(ConfigEnum.PRIVATE_KEY.getType()),configMapper.search(ConfigEnum.PUBLIC_KEY.getType()) )
                            .encryptBase64(param.getPassword(), KeyType.PrivateKey));
                    if (mapper.insert(user) > 0) {
                        infoMapper.insert(new UserInfo(user.getId()));
                        return R.OK();
                    } return R.ERROR("用户注册失败");
                } return R.ERROR("用户名或手机号已存在");
            } return R.ERROR("该手机号未进行验证码校验");
        } return R.ERROR("不能为空");
    }

    @Override
    public R checkPhone(String phone) {
        if (StringUtils.hasLength(phone)) {
            if (PhoneUtil.checkPhone(phone)) {
                if (mapper.selectOne(new QueryWrapper<User>().eq("phone", phone)) == null) {
                    return R.OK();
                }
                return R.ERROR("手机号已存在");
            }
        }
        return R.ERROR("手机号格式不正确");
    }

    @Override
    public R checkName(String name) {
        if (StringUtils.hasLength(name)) {
            if (mapper.selectOne(new QueryWrapper<User>().eq("name", name.trim())) == null) {
                return R.OK();
            } return R.ERROR("用户名已存在");
        } return R.ERROR("用户名为空");
    }

    @Override
    public R all() {
        return R.OK(mapper.selectList(null));
    }

    @Override
    public R loginName(UserLoginNameParam param) {
        //1.校验
        if (param != null) {
            //2.查询数据库 昵称或手机号
            User user = mapper.selectOne(new QueryWrapper<User>().eq("phone", param.getName()).or().eq("name", param.getName()));
            //3.校验用户是否存在
            if (user != null) {
                //4.是否在线
                if (!template.hasKey(RedisKey.AUTH_USER_ONLINE + user.getId())) {
                    user.setPassword("");
                    String token = JwtUtil.createToken(user);
                    //7.存储令牌 设置有效期 30分钟 Redis
                    //记录 在线用户的令牌 -用户获取登录状态
                    template.opsForValue().set(RedisKey.AUTH_USER_TOKEN + token, user.getId().toString(),30, TimeUnit.MINUTES);
                    //记录 在线的用户 -唯一登录
                    template.opsForValue().set(RedisKey.AUTH_USER_ONLINE + user.getId(), token,30,TimeUnit.MINUTES);
                    return R.OK(token);
                } return R.ERROR("您的账号已在其他设备登录");
            } return R.ERROR("用户名不存在");
        } return R.ERROR("请输入用户名和密码");
    }

    @Override
    public R loginCode(UserLoginCodeParam param) {
        //1,校验
        if (param != null) {
            //2.验证是否存在登录的短信验证码
            if (template.hasKey(RedisKey.AUTH_LOGIN_CODE + param.getPhone())) {
                //3.查询数据库
                User user = mapper.selectOne(new QueryWrapper<User>().eq("phone", param.getPhone()));
                //4.校验 账号是否存在
                if (user != null) {
                    //5.是否在线
                    if (template.hasKey(RedisKey.AUTH_USER_ONLINE + user.getId())) {
                        //6.校验 短信验证码
                        if (param.getCode().equals(template.opsForValue().get(RedisKey.AUTH_LOGIN_CODE + param.getPhone()))) {
                            //7.密码校验成功 本次登录成功,生成令牌 JWT算法
                            user.setPassword("");
                            String token = JwtUtil.createToken(user);
                            //8.存储令牌 设置有效期 30分钟 Redis
                            //记录 在线用户的令牌 -用户获取登录状态
                            template.opsForValue().set(RedisKey.AUTH_USER_TOKEN + token, user.getId().toString(),30, TimeUnit.MINUTES);
                            //记录 在线的用户 -唯一登录
                            template.opsForValue().set(RedisKey.AUTH_USER_ONLINE + user.getId(), token,30,TimeUnit.MINUTES);
                            //9.删除登陆验证码
                            template.delete(RedisKey.AUTH_LOGIN_CODE + param.getPhone());
                            msgMapper.insert(new UserMsg(user.getId(), UserMsgType.登录消息.getValue(), "欢迎登陆News系统", "您通过手机验证码的方式完成了登录"));
                            return R.OK(token);
                        } return R.ERROR("验证码已过期");
                    } return R.ERROR("您的账号已在其他设备登录");
                } return R.ERROR("手机号未注册");
            } return R.ERROR("验证码不存在或到期");
        } return R.ERROR("参数不能为空");
    }

    @Override
    public R loginOut(String token) {
        //1.校验
        if (StringUtils.hasLength(token)) {
            //2.校验令牌是否有效
            if (template.hasKey(RedisKey.AUTH_USER_TOKEN + token)) {
                //3.令牌有效  允许退出
                String uid=template.opsForValue().get(RedisKey.AUTH_USER_TOKEN+token);
                template.delete(RedisKey.AUTH_USER_ONLINE+uid);
                template.delete(RedisKey.AUTH_USER_TOKEN+token);
                msgMapper.insert(new UserMsg(Integer.parseInt(uid),UserMsgType.登出消息.getValue(), "退出News系统","您退出了登陆" ));
                //4.返回
                return R.OK();
            }
        } return R.ERROR("未获取到有效的登录状态信息");

    }

    @Override
    public R findPass(UserFindPasswdParam param) {
        //1.校验
        if (param != null) {
            // 2.验证 验证码校验是否成功
            if (template.opsForSet().isMember(RedisKey.AUTH_FINDPHONE, param.getPhone())) {
                //3.密码 转为密文
                String pass = SecureUtil.rsa(configMapper.search(ConfigEnum.PRIVATE_KEY.getType()),configMapper.search(ConfigEnum.PUBLIC_KEY.getType()) )
                        .encryptBase64(param.getNewPassword(), KeyType.PrivateKey);
                //4.修改密码
                if (mapper.updatePass(param.getNewPassword(), param.getPhone()) > 0 ) {
                    template.opsForSet().remove(RedisKey.AUTH_FINDPHONE, param.getPhone());
                    return R.OK();
                } return R.ERROR("密码修改失败");
            } return R.ERROR("验证码校验失败");
        } return R.ERROR("不能为空");
    }

    @Override
    public R updatePass(String token, String password) {
        if (StringUtils.hasLength(password)) {
            String pass = SecureUtil.rsa(configMapper.search(ConfigEnum.PRIVATE_KEY.getType()),configMapper.search(ConfigEnum.PUBLIC_KEY.getType()) )
                    .encryptBase64(password, KeyType.PrivateKey);
            int uid = Integer.parseInt(template.opsForValue().get(RedisKey.AUTH_USER_TOKEN + token));
            if (mapper.updateById(new User(pass,uid)) > 0) {
                template.delete(RedisKey.AUTH_USER_ONLINE+uid);
                template.delete(RedisKey.AUTH_USER_TOKEN+token);
                return R.OK();
            } return R.OK("密码修改失败");
        } return R.ERROR("密码不能为空");
    }
}
